#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <cstdlib>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <sys/wait.h>

#include "Util.hpp"
#include "Log.hpp"

#define SEP ": "
#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"

static std::string Code2Desc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default:
        desc = "Not Found";
        break;
    }
    return desc;
}

static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/x-javascript"},
        {"png", "application/x-png"},
        {"gif", "image/gif"},
        {"ico", "image/x-icon"},
        {"svg", "text/xml"}
    };
    auto it = suffix2desc.find(suffix);
    if(it != suffix2desc.end())
    {
        return it->second;
    }
    return "text/html";
}

class HttpRequest
{
  public:
    std::string request_line; //请求行
    std::vector<std::string> request_header;  //请求报头
    std::string blank;  //空行
    std::string request_body; //请求正文
    //解析请求行之后的结果
    std::string method; //请求方法                                                                                           
    std::string uri;  //唯一资源定位符: path?args
    std::string version;  //http版本
    //解析报文头为key:value结构
    std::unordered_map<std::string, std::string> header_kv;
    int content_length; //正文长度
    std::string path;   //资源路径
    std::string query_string;   //参数
    bool cgi;   //是否使用通用网关接口
    std::string suffix; //文件后缀
    int size;   //资源大小
  public:
    HttpRequest()
        : blank(LINE_END)
        , content_length(0)
        , cgi(false) 
    {}
    ~HttpRequest()
    {}
};
class HttpResponse
{
  public:
    std::string status_line;  //状态行
    std::vector<std::string> response_header; //响应报头
    std::string blank;  //空行
    std::string response_body;  //响应正文
    int status_code;    //状态码
    int fd; //请求资源的文件描述符
  public:
    HttpResponse()
        : status_code(OK)
        , fd(-1)
    {}
    ~HttpResponse()
    {}
};
//EndPoint功能：读取请求、分析请求、构建响应、IO通信
class EndPoint
{
  public:
    EndPoint(int sock)
        : _sock(sock)
        , _stop(false)
    {}
    bool RecvHttpRequest()
    {
        if((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))  //接受请求行和报文头
        {
            ParseHttpRequestLine(); //解析请求行
            ParseHttpRequestHeader();   //解析报文头
            RecvHttpRequestBody();   //读取正文
        }
        return _stop;
    }
    bool IsStop()
    {
        return _stop;
    }
    void BuildHttpResponse()
    {
        std::string &method = _http_request.method;
        int &code = _http_response.status_code;
        size_t point_pos = 0;   //文件后缀名的位置
        if(method != "GET" && method != "POST")
        {
            //非法请求
            LOG(WARNING, "method is not right");
            code = BAD_REQUEST;
            goto END;
            // return;
        }
        if(method == "GET")
        {
            size_t pos = _http_request.uri.find('?');
            if(pos != std::string::npos)
            {
                Util::CutString(_http_request.uri, _http_request.path, _http_request.query_string, "?");
                _http_request.cgi = true;   //GET方法带参使用cgi
            }
            else 
            {
                _http_request.path = _http_request.uri;
            }
        }
        else 
        {
            //POST
            _http_request.cgi = true;
            _http_request.path = _http_request.uri;
        }
        _http_request.path = WEB_ROOT + _http_request.path;
        if(_http_request.path[_http_request.path.size() - 1] == '/')
        {
            _http_request.path += HOME_PAGE;
        }
        struct stat st;
        if(stat(_http_request.path.c_str(), &st) == 0)
        {
            //该资源存在
            if(S_ISDIR(st.st_mode))
            {
                //说明请求的资源是一个目录
                _http_request.path += "/";
                _http_request.path += HOME_PAGE;
                LOG(INFO, _http_request.path);
                //更新资源属性
                stat(_http_request.path.c_str(), &st);
            }                                          
            if(st.st_mode & S_IXUSR
            || st.st_mode & S_IXGRP
            || st.st_mode & S_IXOTH)
            {
                //可执行程序特殊处理
                _http_request.cgi = true;
            }
            _http_request.size = st.st_size;
        }
        else
        {
            LOG(WARNING, _http_request.path + " Not Found!");
            code = NOT_FOUND;
            goto END;
            // return;
        }
        point_pos = _http_request.path.rfind('.');
        if(point_pos == std::string::npos)
        {
            _http_request.suffix = ".html";
        }
        else 
        {
            _http_request.suffix = _http_request.path.substr(point_pos);
        }
        LOG(DEBUG, "Debug suffix: " + _http_request.suffix);
        if(_http_request.cgi)
        {
            code = ProcessCGI();
        }
        else 
        {
            //返回静态网页
            code = ProcessNonCGI();
        }
END:
        BuildHttpResponseHelper();       
    }
    void SendHttpResponse()
    {
        auto &status_code = _http_response.status_code;
        send(_sock, _http_response.status_line.c_str(), _http_response.status_line.size(), 0);
        for(auto &it: _http_response.response_header)
        {
            send(_sock, it.c_str(), it.size(), 0);
        }
        send(_sock, "\r\n", sizeof("\r\n"), 0);
        // send(_sock, _http_response.blank.c_str(), _http_response.blank.size(), 0);   //不知道为什么这行代码没用
        if(_http_request.cgi)
        {
            //send body
            auto &response_body = _http_response.response_body;
            size_t total = 0;
            size_t size = 0;
            const char *start = response_body.c_str();
            while(total < response_body.size() && (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
                total += size;
            }
        }
        else 
        {
            sendfile(_sock/*发到哪*/, _http_response.fd/*从哪读*/, nullptr/*偏移量*/, _http_request.size/*发送字节个数*/);
            close(_http_response.fd);
        }
    }
    ~EndPoint()
    {
        if(_sock >= 0)
        {
            close(_sock);
        }
    }
  private:
    bool RecvHttpRequestLine()
    {
        auto &line = _http_request.request_line;
        if(Util::ReadLine(_sock, line) > 0)
        {
            line.resize(line.size() - 1);   
            // LOG(DEBUG, line);
        } 
        else 
        {
            _stop = true;
        }
        return _stop;
    }
    bool RecvHttpRequestHeader()
    {
        std::string line;
        while(true)
        {
            line.clear();
            if(Util::ReadLine(_sock, line) <= 0)
            {
                _stop = true;
                break;
            }
            if(line == "\n")
            {
                _http_request.blank = line;
                break;
            }
            line.resize(line.size() - 1);
            _http_request.request_header.push_back(line);
            // LOG(DEBUG, line);
        }
        return _stop;
    }
    void ParseHttpRequestLine()
    {
        auto &line = _http_request.request_line;
        std::stringstream ss(line); //默认以空格切分字符串
        ss >> _http_request.method >> _http_request.uri >> _http_request.version;  
        auto &method = _http_request.method;   
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);   
    }
    void ParseHttpRequestHeader()
    {
        std::string key, value;
        for(auto &it: _http_request.request_header)
        {
            //以冒号空格为分隔符切分
            if(Util::CutString(it, key, value, SEP))
            {
                _http_request.header_kv.insert({key, value});
            }
        }
    }
    bool IsNeedRecvHttpRequestBody()
    {
        if(_http_request.method == "POST")
        {
            _http_request.content_length = atoi(_http_request.header_kv["Content-Length"].c_str());
            LOG(INFO, _http_request.header_kv["Content-Length"]);
            return true;
        }
        return false;
    }
    bool RecvHttpRequestBody()
    {
        if(IsNeedRecvHttpRequestBody())
        {
            int content_length = _http_request.content_length;
            char ch = 0;
            while(content_length)
            {
                ssize_t s = recv(_sock, &ch, 1, O_RDONLY);
                if(s > 0)
                {
                    _http_request.request_body += ch;
                    content_length--;
                }
                else 
                {
                    _stop = true;
                    break;
                }
            }
            LOG(INFO, _http_request.request_body);
        }
        return _stop;
    }
    int ProcessNonCGI()
    {
        _http_response.fd = open(_http_request.path.c_str(), O_RDONLY);
        if(_http_response.fd >= 0)
        {
            // auto &status_line = _http_response.status_line;
            // status_line = HTTP_VERSION;
            // status_line += " ";
            // status_line += std::to_string(_http_response.status_code);
            // status_line += " ";
            // status_line += Code2Desc(_http_response.status_code);
            // status_line += LINE_END;
            // //将正文类型和正文长度获取并存放进响应报文中
            // std::string content_type_string = "Content-Type: ";
            // content_type_string += Suffix2Desc(_http_request.suffix);
            // content_type_string += LINE_END;
            // _http_response.response_header.push_back(content_type_string);
            // std::string content_length_string = "Content-Length: ";
            // content_length_string += std::to_string(_http_response.size);
            // content_length_string += LINE_END;
            // _http_response.response_header.push_back(content_length_string);
            LOG(INFO, _http_request.path + " open success");
            return OK;
        }
        return NOT_FOUND;
    }
    int ProcessCGI()
    {
        LOG(INFO, "Debug CGI");
        auto &method = _http_request.method;
        std::string query_string_env;   //GET方式以环境变量与子进程通信
        std::string method_env; //环境变量方式告知子进程请求方法
        std::string content_length_env; //环境变量方式告知子进程正文长度
        int code = OK;  //父进程等待子进程退出是否成功
        //父进程数据
        auto &query_string = _http_request.query_string;    //GET
        auto &body_text = _http_request.request_body;   //POST
        auto &bin = _http_request.path; //子进程执行的目标
        int content_length = _http_request.content_length;
        auto &response_body = _http_response.response_body;
        //以父进程的视角创建两根管道
        int input[2];   //读取
        int output[2];  //写入
        if(pipe(input) < 0)
        {
            LOG(ERROR, "create input pipe error");
            return SERVER_ERROR;
        }
        if(pipe(output) < 0)
        {
            LOG(ERROR, "create output pipe error");
            return SERVER_ERROR;
        }
        pid_t pid = fork();
        if(pid == 0)  
        {
            //child exec*
            close(input[0]);
            close(output[1]);
            //方法放置到环境变量中
            method_env = "METHOD=";
            method_env += method;  
            LOG(INFO, method_env);
            putenv((char*)method_env.c_str());
            //通过环境变量获取参数
            if(method == "GET")
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char*)query_string_env.c_str());
            }
            else if(method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char*)content_length_env.c_str());
            }
            LOG(INFO, "bin: " + bin);
            //重定向
            dup2(output[0], 0);
            dup2(input[1], 1);
            execl(bin.c_str(), bin.c_str(), nullptr);
            LOG(FATAL, "execl error");
            code = NOT_FOUND;
        }
        else if (pid < 0)
        {
            LOG(ERROR, "fork erro!");
            return NOT_FOUND;
        }
        else 
        {
            //parent
            LOG(INFO, "parent process begin");
            close(input[1]);    //not write in input
            close(output[0]);   //not read in output
            if(method == "POST")
            {
                int size = 0;   //单次写入的字符数
                int total = 0;  //总共写入的字符数
                const char *start = body_text.c_str();
                while(total < content_length && (size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
                    total += size;
                }
            }
            char ch = 0;
            while(read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if(ret == pid)
            {
                if(WIFEXITED(status))   //子进程退出正常
                {
                    if(WEXITSTATUS(status) == 0)    //父进程等待正常
                    {
                        code = OK;
                    }
                    else 
                    {
                        code = BAD_REQUEST;
                    }
                }
                else 
                {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
            LOG(INFO, "parent close");
        }
        return code;
    } 
    void HandlerError(std::string page)
    {
        LOG(INFO, "HandlerError debug begin");
        LOG(INFO, page);
        //给用户返回404页面
        _http_request.cgi = false;
        _http_response.fd = open(page.c_str(), O_RDONLY);
        if(_http_response.fd >= 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            _http_request.size = st.st_size; 
            std::string line = "Content-Type: text/html";
            line += LINE_END;
            _http_response.response_header.push_back(line);
            
            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_response.response_header.push_back(line);
        }
        else
        {
            LOG(FATAL, "open error");
        }
    }
    void BuildOkResponse()
    {
        std::string line = "Content-Type: ";
        line += Suffix2Desc(_http_request.suffix);
        line += LINE_END;
        _http_response.response_header.push_back(line);
        line.clear();
        line = "Content-Length: ";
        if(_http_request.cgi)
        {
            line += std::to_string(_http_response.response_body.size());
        }
        else 
        {
            line += std::to_string(_http_request.size);
        }
        line += LINE_END;
        _http_response.response_header.push_back(line);
    }
    void BuildHttpResponseHelper()
    {
        //构建状态行
        auto &code = _http_response.status_code;
        auto &status_line = _http_response.status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";

        status_line += Code2Desc(code);
        status_line += LINE_END; 

        std::string path = WEB_ROOT;
        path += "/";
        switch(code)
        {
          case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
          case OK:
            BuildOkResponse();
            break;
          case BAD_REQUEST: //临时方案
            path += PAGE_404;
            HandlerError(path);
            break;
          case SERVER_ERROR:    //临时方案
            path += PAGE_404;
            HandlerError(path);
            break;
          default:  //临时方案
            path += PAGE_404;
            HandlerError(path);
            break;
        }
    }
  private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response; 
    bool _stop; //是否要退出
};

class CallBack
{
  public:
    CallBack()
    {}
    void operator()(int sock)
    {
        HandlerRequest(sock);
    }
    void HandlerRequest(int sock)
    {
        LOG(INFO, "Hander Request Begin");
        // int _sock = *(int*)sock;    //临时方案
        // delete (int*)sock;
        std::cout << "get a new link ... sock: " << sock << std::endl;
// #define DEBUG_HTTP_RECV_HEADER 1
#ifdef DEBUG_HTTP_RECV_HEADER
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << "------------------------begin------------------------" << std::endl;
        std::cout << buffer << std::endl;
        std::cout << "------------------------end------------------------" << std::endl;
#else
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        if(!ep->IsStop())
        {
            LOG(INFO, "RECV SUCCESS, BEGIN BUILD AND SEND");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }  
        else 
        {
            LOG(WARNING, "RECV ERROR, STOP BUILD AND SEND");
        }
        delete ep;
#endif

        // std::string line;
        // Util::ReadLine(_sock, line);
        // std::cout << line << std::endl;
        // close(_sock);
        LOG(INFO, "Hander Request End");
    }
    ~CallBack()
    {}
};
